package spoon.service;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;

import spoon.core.domain.Game;
import spoon.core.exception.ConvertException;
import spoon.core.service.HttpService;
import spoon.dao.MarathonDao;

public abstract class MarathonAbstractService implements MarathonService {

	@Autowired
	protected HttpService httpService;

	@Autowired
	protected MarathonDao marathonDao;

	@Value("#{marathon['site']}")
	protected String SITE;

	@Value("#{marathon['url']}")
	protected String URL;

	@Value("#{marathon['link']}")
	protected String LINK;

	protected String GATE_URL;

	protected String ITEM_CODE;

	protected String ITEM_NAME;

	protected Map<String, String> leagueMap;

	protected Map<String, String> teamMap;

	protected Logger log = LoggerFactory.getLogger(this.getClass());

	@Override
	public Elements getLeagueList() {
		log.info("===================================================================");
		log.info("    {} {} 리그 정보 검색 시작", SITE, ITEM_CODE);

		String source = httpService.getSource(GATE_URL);
		Elements elements = Jsoup.parseBodyFragment(source).select(".main-block-events");
		// 첫번째 리그는 가상조합이므로 제거한다.
		elements.remove(0);

		log.info("    {} {} 리그 정보 검색 종료. 전체 추가 리그수 = {}", SITE, ITEM_CODE, elements.size());
		log.info("===================================================================");

		return elements;
	}

	@Override
	public abstract void getGame(Element element);

	/**
	 * 마지막에 찍힌 콤마(.)를 제거한다.
	 * 
	 * @param source
	 * @return
	 */
	protected String removeLast(String source) {
		if (source.endsWith(".")) {
			source = source.substring(0, source.length() - 1);
		}
		return source;
	}

	/**
	 * 국가, 리그명, 게임아이디 등을 설정한다.
	 * 
	 * @param league
	 * @param elements
	 */
	protected void setLeagueInfo(Game league, Elements elements) {
		league.setNation(removeLast(elements.get(1).text()));
		String leagueName = removeLast(elements.get(2).text());
		if (elements.size() > 3) { // 추가 하위 정보가 있는 경우
			leagueName = leagueName + " " + elements.get(3).text();
		}

		league.setLeagueName(leagueName);
		league.setLeagueCode(getLeagueCode(leagueName));
	}

	/**
	 * 리그코드 변경
	 * 
	 * @param league
	 * @return
	 */
	protected String getLeagueCode(String league) {
		String leagueCode = leagueMap.get(league);
		if (leagueCode == null || "".equals(leagueCode)) {
			leagueCode = league;
			// log.error("{} {} 리그코드 변경에러, 리그명 = {}", SITE, ITEM_NAME, league);
		}
		return leagueCode;
	}

	/**
	 * 팀코드 변경
	 * 
	 * @param team
	 * @return
	 */
	protected String getTeamCode(String team) {
		String teamCode = teamMap.get(team);
		if (teamCode == null || "".equals(teamCode)) {
			teamCode = team;
			// log.error("{} {} 팀코드 변경에러, 팀명 = {}", SITE, ITEM_NAME, team);
		}
		return teamCode;
	}

	/**
	 * 날짜 변환 표준시간으로 변환
	 * 
	 * @param dt
	 * @return
	 */
	protected Date convertDate(String dt) {

		DateFormat df = new SimpleDateFormat("yyyydd MMM HH:mm", Locale.UK);
		TimeZone tz = TimeZone.getTimeZone("UK");
		df.setTimeZone(tz);

		Date date = new Date();
		try {
			date = df.parse(Calendar.getInstance(Locale.UK).get(Calendar.YEAR) + dt);
		} catch (ParseException e) {
			throw new ConvertException(new Throwable(SITE + " " + ITEM_NAME + " 날짜변경 에러 (" + dt + ")", e));
		}

		return date;
	}

	/**
	 * 날짜 변환 표준시간으로 변환
	 * 
	 * @param dt
	 * @return
	 */
	protected Date convertTodayDate(String dt) {

		DateFormat df = new SimpleDateFormat("yyyy M d HH:mm", Locale.UK);
		TimeZone tz = TimeZone.getTimeZone("UK");

		df.setTimeZone(tz);
		Calendar cal = Calendar.getInstance(Locale.UK);

		dt = cal.get(Calendar.YEAR) + " " + (cal.get(Calendar.MONTH) + 1) + " " + cal.get(Calendar.DATE) + " " + dt;
		Date date = new Date();

		try {
			date = df.parse(dt);
		} catch (ParseException e) {
			throw new ConvertException(new Throwable(SITE + " " + ITEM_NAME + " 날짜변경 에러 (" + dt + ")", e));
		}

		return date;
	}

	/**
	 * 배당변환 분수 형태를 소수점 형태로
	 * 
	 * @param rate
	 * @return
	 */
	protected Double convertRate(String rate) {
		if (rate == null || "".equals("0")) {
			rate = "0/1";
		}
		String[] s = rate.split("/");

		if (s.length != 2) {
			throw new ConvertException(new Throwable(SITE + " " + ITEM_NAME + " 배당율 변경 에러 (" + rate + ")"));
		}
		Double d = 1D + (Double.parseDouble(s[0]) / Double.parseDouble(s[1]));

		return Double.parseDouble(String.format("%.2f", d - 0.005));
	}

	/**
	 * 핸디캡 변환
	 * 
	 * @param handicap
	 * @return
	 */
	protected Double convertHandicap(String handicap) {
		handicap = handicap.replace("(", "").replace(")", "");

		Double d = null;

		if (handicap == null || "".equals(handicap)) {
			d = 0D;
		} else if (handicap.contains(",")) {
			String[] hs = handicap.split(",");
			d = (Double.parseDouble(hs[0]) + Double.parseDouble(hs[1])) / 2D;
		} else {
			d = Double.parseDouble(handicap);
		}

		return d;
	}

	/**
	 * 핸디캡 홈팀, 원정팀은 class="aleft"다.
	 * 
	 * @param game
	 * @param map
	 * @param elements
	 */
	protected void betHandicap(Game game, Map<String, String> map, Elements elements) {
		Game betting;

		int i = 0;
		for (Element td : elements) {
			// 홈팀 원정팀 시작
			if (td.hasClass("aleft")) {
				i++;
				continue;
			}

			// 빈 td
			if (td.text().equals("")) {
				continue;
			}

			betting = new Game();

			betting.setBetTarget(map.get("betTarget"));
			betting.setBetTime(map.get("betTime"));
			betting.setBetType(map.get("betType"));
			betting.setBetName(map.get("betName"));

			if (i == 1) { // 홈팀
				betting.setBetRateCode("1");
				betting.setBetRateName(game.getHomeTeamName());
			} else if (i == 2) { // 원정팀
				betting.setBetRateCode("2");
				betting.setBetRateName(game.getAwayTeamName());
			} else {
				log.error("핸디캡 에러 : {}", elements.html());
			}
			betting.setHandicap(convertHandicap(td.ownText()));
			betting.setHandicapName(td.ownText());
			betting.setBetRate(convertRate(td.select("span").text()));
			game.addGameList(betting);

		}
	}

	protected void betOverUnder(Game game, Map<String, String> map, Elements elements) {
		Game betting;

		// 첫번째 tr은 th 핸디캡이므로 제거한다.
		Elements ths = elements.remove(0).select("th");
		String handicapCode = "";
		for (Element tr : elements) {
			Elements td = tr.select("td");
			for (Element th : ths) {
				String handicap = th.text();
				// 홀짝은 취급하지 않는다.
				if ("Odd".equals(handicap) || "Even".equals(handicap)) {
					continue;
				}
				// handicapCode
				int idx = ths.indexOf(th);
				String betRate = td.get(idx).text();
				if ("".equals(handicap)) {
					handicapCode = betRate;
					continue;
				}
				// 배당이 없다면 건너뛰자
				if ("".equals(betRate)) {
					continue;
				}

				betting = new Game();
				betting.setBetTarget(map.get("betTarget"));
				betting.setBetTime(map.get("betTime"));
				betting.setBetType(map.get("betType"));
				betting.setBetName(map.get("betName"));
				betting.setBetRateCode(handicapCode);
				betting.setBetRateName(betRate);
				betting.setHandicap(convertHandicap(handicap));
				betting.setHandicapName(handicap);
				betting.setBetRate(convertRate(betRate));
				game.addGameList(betting);
			}
		}
	}

	/**
	 * Half Markets td의 순서대로 1, 1X, X, 12, 2, X2의 순서
	 * 
	 * @param game
	 * @param map
	 * @param elements
	 */
	protected void betHalfMatch(Game game, Map<String, String> map, Elements elements) {
		if (elements.size() != 6)
			return;

		Game betting;

		// 1
		if (!"".equals(elements.get(0).ownText())) {
			betting = new Game();
			betting.setBetTarget(map.get("betTarget"));
			betting.setBetTime(map.get("betTime"));
			betting.setBetType("1X2");
			betting.setBetName("1st Half Result");
			betting.setHandicap(0D);
			betting.setHandicapName("");
			betting.setBetRateCode("1");
			betting.setBetRateName(elements.get(0).ownText());
			betting.setBetRate(convertRate(elements.get(0).select("span").text()));
			game.addGameList(betting);
		}

		// 1X
		if (!"".equals(elements.get(1).ownText())) {
			betting = new Game();
			betting.setBetTarget(map.get("betTarget"));
			betting.setBetTime(map.get("betTime"));
			betting.setBetType("DC");
			betting.setBetName("1st Half Double Chance");
			betting.setHandicap(0D);
			betting.setHandicapName("");
			betting.setBetRateCode("1X");
			betting.setBetRateName(elements.get(1).ownText());
			betting.setBetRate(convertRate(elements.get(1).select("span").text()));
			game.addGameList(betting);
		}

		// X
		if (!"".equals(elements.get(2).ownText())) {
			betting = new Game();
			betting.setBetTarget(map.get("betTarget"));
			betting.setBetTime(map.get("betTime"));
			betting.setBetType("1X2");
			betting.setBetName("1st Half Result");
			betting.setHandicap(0D);
			betting.setHandicapName("");
			betting.setBetRateCode("X");
			betting.setBetRateName(elements.get(2).ownText());
			betting.setBetRate(convertRate(elements.get(2).select("span").text()));
			game.addGameList(betting);
		}

		// 12
		if (!"".equals(elements.get(3).ownText())) {
			betting = new Game();
			betting.setBetTarget(map.get("betTarget"));
			betting.setBetTime(map.get("betTime"));
			betting.setBetType("DC");
			betting.setBetName("1st Half Double Chance");
			betting.setHandicap(0D);
			betting.setHandicapName("");
			betting.setBetRateCode("12");
			betting.setBetRateName(elements.get(3).ownText());
			betting.setBetRate(convertRate(elements.get(3).select("span").text()));
			game.addGameList(betting);
		}

		// 2
		if (!"".equals(elements.get(4).ownText())) {
			betting = new Game();
			betting.setBetTarget(map.get("betTarget"));
			betting.setBetTime(map.get("betTime"));
			betting.setBetType("2");
			betting.setBetName("1st Half Result");
			betting.setHandicap(0D);
			betting.setHandicapName("");
			betting.setBetRateCode("2");
			betting.setBetRateName(elements.get(4).ownText());
			betting.setBetRate(convertRate(elements.get(4).select("span").text()));
			game.addGameList(betting);
		}

		// X2
		if (!"".equals(elements.get(5).ownText())) {
			betting = new Game();
			betting.setBetTarget(map.get("betTarget"));
			betting.setBetTime(map.get("betTime"));
			betting.setBetType("DC");
			betting.setBetName("1st Half Double Chance");
			betting.setHandicap(0D);
			betting.setHandicapName("");
			betting.setBetRateCode("X2");
			betting.setBetRateName(elements.get(5).ownText());
			betting.setBetRate(convertRate(elements.get(5).select("span").text()));
			game.addGameList(betting);
		}

	}

	/**
	 * 디비 입력
	 * 
	 * @param league
	 * @return
	 */
	@Transactional
	protected int addBetting(Game league) {

		marathonDao.delete(league);
		return marathonDao.addBetting(league);

	}

}
